package org.misalen.web.database.dao.mysql.impl;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.List;

import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.misalen.web.database.dao.mysql.BaseMySqlDao;
import org.misalen.web.database.model.mysql.BaseSqlModel;
import org.misalen.web.database.util.PaginationFrom;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 基础数据库实现类
 * 
 * @author 赵国超
 * 
 * @param <T>
 */
@SuppressWarnings("all")
public abstract class BaseMySqlDaoImpl<T extends BaseSqlModel> implements BaseMySqlDao<T> {
	protected final static String LIKE_CONDITION = "%%%1s%%";
	protected final SimpleDateFormat dd = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
	private SessionFactory sessionFactory;

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	protected Session openSession() {
		return sessionFactory.openSession();
	}

	/* ========================================================== */
	@Override
	public Serializable save(T allclass) {
		Session session = this.openSession();
		Serializable serializable = session.save(allclass);
		closeSession(session);
		return serializable;
	}

	@Override
	public void delete(T allclass) {
		Session session = this.openSession();
		session.delete(allclass);
		closeSession(session);
	}

	@Override
	public void delete(Class<T> allclass, String primaryKey) {
		Session session = this.openSession();
		Table table = allclass.getAnnotation(Table.class);
		String sql;
		if (table != null && table.name() != null && table.name().length() > 0) {
			sql = "delete FROM " + table.name();
		} else {
			sql = "delete FROM " + allclass.getSimpleName();
		}
		if (primaryKey != null) {
			sql += "  where primaryKey = '" + primaryKey + "'";
		}
		session.createSQLQuery(sql).executeUpdate();
		closeSession(session);
	}

	@Override
	public void update(T allclass) {
		Session session = this.openSession();
		session.update(allclass);
		closeSession(session);
	}

	@Override
	public void updateNotNull(T allclass) {
		Session session = this.openSession();
		Query query = session.createQuery(getUpdateSql(allclass));
		query.executeUpdate();
		closeSession(session);
	}

	@Override
	public PaginationFrom<T> findPageBySql(String sql, Object[] param, PaginationFrom<T> pagination,
			Class<T> allclass) {
		Session session = this.openSession();
		SQLQuery query = session.createSQLQuery(sql);
		if (allclass != null) {
			query.addEntity(allclass);
		}
		setParameterList(query, param);
		if (pagination != null) {
			setResultIndex(query, pagination.getPage(), pagination.getRows());
		} else {
			pagination = new PaginationFrom<T>();
			pagination.setRows(0);
			pagination.setPage(0);
		}
		List<T> list = query.list();
		pagination.setList(list);
		closeSession(session);
		return pagination;
	}

	@Override
	public PaginationFrom<?> findPageByHql(String hql, Object[] param, PaginationFrom<T> pagination) {
		Session session = this.openSession();
		Query query = session.createQuery(hql);
		setParameterList(query, param);
		if (pagination != null) {
			setResultIndex(query, pagination.getPage(), pagination.getRows());
		} else {
			pagination = new PaginationFrom<T>();
			pagination.setRows(0);
			pagination.setPage(0);
		}
		List<T> list = query.list();
		pagination.setList(list);
		closeSession(session);
		return pagination;
	}

	@Override
	public PaginationFrom<T> findPageByBean(T allclass, PaginationFrom<T> pagination) {
		Session session = this.openSession();
		Criteria criteria = session.createCriteria(allclass.getClass(), "a");
		setBeanCriteria(criteria, allclass);
		if (pagination != null) {
			pagination.setTotal(Long.valueOf(criteria.setProjection(Projections.rowCount()).uniqueResult().toString()));
			criteria.setProjection(null);
			if (pagination.getAsc() != null) {
				for (String filed : pagination.getAsc()) {
					criteria.addOrder(Order.asc(filed));
				}

			}
			if (pagination.getDesc() != null) {
				for (String filed : pagination.getDesc()) {
					criteria.addOrder(Order.desc(filed));
				}
			}
			if (pagination.getAsc() == null && pagination.getDesc() == null) {
				criteria.addOrder(Order.desc("addTime"));
			}
			setResultIndex(criteria, pagination.getPage(), pagination.getRows());
		} else {
			pagination = new PaginationFrom<T>();
			pagination.setRows(0);
			pagination.setPage(0);
			pagination.setTotal(0l);
		}
		List<T> list = criteria.list();
		pagination.setList(list);
		closeSession(session);
		return pagination;
	}

	@Override
	public T get(Class<T> allclass, String id) {
		Session session = this.openSession();
		T t = (T) session.get(allclass, id);
		closeSession(session);
		return t;
	}

	@Override
	public T get(String hql, Object[] param) {
		Session session = this.openSession();
		Query query = session.createQuery(hql);
		setParameterList(query, param);
		setResultIndex(query, 1, 1);
		List<T> list = query.list();
		closeSession(session);
		return list.size() >= 1 ? list.get(0) : null;
	}

	@Override
	public Long count(String hql, Object[] param) {
		Session session = this.openSession();
		Query query = session.createQuery(hql);
		setParameterList(query, param);
		Long numLong = (Long) query.uniqueResult();
		closeSession(session);
		return numLong;
	}

	/**
	 * 根据bean 生成 Criteria的where 条件
	 * 
	 * @param criteria
	 * @param allclass
	 */
	protected void setBeanCriteria(Criteria criteria, T allclass) {
		try {
			Field[] fields = allclass.getClass().getDeclaredFields();
			for (Field field : fields) {
				field.setAccessible(true);
				String type = field.getType().getSimpleName();
				Object val = field.get(allclass);
				if (val == null || String.valueOf(val).equals("")) {

				} else if (type.equals("Date")) {

				} else if (type.equals("Long")) {
					criteria.add(Restrictions.eq(field.getName(), val));
				} else {
					criteria.add(Restrictions.like(field.getName(), String.format(LIKE_CONDITION, val)));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 根据bean 生成 Criteria的where 条件
	 * 
	 * @param criteria
	 * @param allclass
	 */
	protected String getHqlWhere(T allclass) {
		StringBuffer buffer = new StringBuffer();
		buffer.append(" where 1=1 ");
		try {
			Field[] fields = allclass.getClass().getDeclaredFields();
			for (Field field : fields) {
				field.setAccessible(true);
				String type = field.getType().getSimpleName();
				Object val = field.get(allclass);
				if (val == null || String.valueOf(val).equals("")) {

				} else if (type.equals("Date")) {

				} else if (type.equals("Long")) {
					buffer.append("and a.");
					buffer.append(field.getName());
					buffer.append(" =");
					buffer.append(val);
					buffer.append(" ");
				} else {
					buffer.append("and a.");
					buffer.append(field.getName());
					buffer.append(" like '");
					buffer.append(String.format(LIKE_CONDITION, val));
					buffer.append("' ");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return buffer.toString();
	}

	/**
	 * 根据bean 生成 update 语句
	 * 
	 * @param allclass
	 * @return
	 */
	protected String getUpdateSql(T allclass) {
		try {
			String hql = "update " + allclass.getClass().getSimpleName() + " set ";
			String where = null;
			Field[] fields2 = allclass.getClass().getSuperclass().getDeclaredFields();
			for (Field field : fields2) {
				field.setAccessible(true);
				Object val = field.get(allclass);
				String type = field.getType().getSimpleName();
				if (field.getAnnotation(Id.class) != null) {
					where = " where " + field.getName() + "='" + val + "'";
				} else if (val != null) {
					if (type.equals("Date")) {
						hql += field.getName() + "='" + dd.format(val) + "',";
					}
				}
			}
			Field[] fields = allclass.getClass().getDeclaredFields();
			for (Field field : fields) {
				field.setAccessible(true);
				String type = field.getType().getSimpleName();
				Object val = field.get(allclass);
				if (val != null) {
					if (type.equals("Date")) {
						hql += field.getName() + "='" + dd.format(val) + "',";
					} else {
						hql += field.getName() + "='" + val + "',";
					}
				}
			}
			hql = hql.substring(0, hql.length() - 1) + where;
			return hql;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 设置 Query 分页信息
	 * 
	 * @param query
	 * @param page
	 * @param rows
	 */
	protected void setResultIndex(Query query, Integer page, Integer rows) {
		if (page != null && page > 0 && rows != null && rows > 0) {
			query = query.setFirstResult((page - 1) * rows).setMaxResults(rows);
		}
	}

	/**
	 * 设置Criteria 分页信息
	 * 
	 * @param criteria
	 * @param page
	 * @param rows
	 */
	protected void setResultIndex(Criteria criteria, Integer page, Integer rows) {
		if (page != null && page > 0 && rows != null && rows > 0) {
			criteria = criteria.setFirstResult((page - 1) * rows).setMaxResults(rows);
		}
	}

	/**
	 * 设置参数 替换
	 * 
	 * @param query
	 * @param param
	 */
	protected void setParameterList(Query query, Object[] param) {
		if (param != null) {
			for (int i = 0; i < param.length; i++) {
				Object obj = param[i];
				if (obj instanceof Collection<?>) {
					query.setParameterList(String.valueOf(i), (Collection<?>) obj);
				} else if (obj instanceof Object[]) {
					query.setParameterList(String.valueOf(i), (Object[]) obj);
				} else {
					query.setParameter(String.valueOf(i), obj);
				}
			}
		}
	}

	protected void closeSession(Session session) {
		session.flush();
		session.clear();
		session.close();
	}
}